#!/bin/bash

menu="fzf"
videoplayer="mpv --no-terminal"
art="./art.txt"
if [ ! -f $art ]; then
	art="/usr/share/ytui/art.txt"
fi
instance=https://invidious.namazso.eu
RSSinstance=https://invidious.namazso.eu
videoInstance=https://yewtu.be
cachedir=/tmp/ytui
if [ ! -d $cachedir ]; then
	mkdir $cachedir
fi
configdir=~/.config/ytui
instances=$(cat $cachedir/instances)
subsfeeds=$configdir/rssfeedupdate
subsfeedsdir=$configdir/rssfeeds
if [ ! -d $configdir ]; then
	mkdir $configdir
fi
freetubesubs=~/.config/FreeTube/profiles.db
subsfile=$configdir/subscriptions.csv
playimmediate=true

green='\e[32m'
blue='\e[34m'
red='\e[31m'
white='\e[0m'
clear='\e[H\e[J'
bold='\e[1m'
PS3="> "

settingsMenu () {
	heading 'Settings'
	echo -ne "
f) get instances
i) data instance [$instance]
r) RSS instance [$RSSinstance]
v) video instance [$videoInstance]
s) import subscriptions from FreeTube
y) import subscriptions from YouTube export
q) Back

> "
	read -n 1 option
	case $option in
		f) getInstances ; settingsMenu ;;
		i) changeInstance ; settingsMenu ;;
		r) changeRSSInstance ; settingsMenu ;;
		v) changeVideoInstance ; settingsMenu ;;
		s) importFreetube ;;
		y) importYoutube ;;
		$'\e'|'q') return ;;
		*) settingsMenu ;;
	esac
}

getInstances() {
	heading "Finding Instances"
	all_instances="$(echo "$(cat $configdir/instances)
$(get 'https://api.invidious.io/instances.json' | \
		jq -r '.[] | .[1].uri' | \
		sed 's#/$##' | sed '/\.onion$/d')" | sort | uniq)"
	while read uri ; do
		testSite $uri
	done <<< $all_instances
	echo "$instances" > $cachedir/instances
}

testSite() {
	echo -ne "$white$uri\t"
	wget --spider --timeout=0.3 -t 3 -q $uri
	if [ $? -eq 0 ]; then
		echo -e "$green Online"
		instances="$instances
$uri"
	else
		echo -e "$red Offline"
	fi;
}

changeInstance () {
	heading "Choose an instance"

	instance_tmp=$instance
	instance=$($menu <<< "$instances")
	if [ -z "$instance" ]; then
		instance=$instance_tmp
	fi
}
changeRSSInstance () {
	heading "Choose an instance"

	instance_tmp=$RSSinstance
	RSSinstance=$($menu <<< "$instances")
	if [ -z "$RSSinstance" ]; then
		RSSinstance=$instance_tmp
	fi
}
changeVideoInstance () {
	heading "Choose an instance"
	videoInstance_tmp=$videoInstance
	videoInstance=$($menu <<< "$instances")
	if [ -z "$videoInstance" ]; then
		videoInstance=$videoInstance_tmp
	fi
}

importFreetube () {
	if [ ! -f $freetubesubs ]; then
		echo "Cannot find freetube Subscriptions in $freetubesubs"
		echo -ne "Enter a different file to try >"
		read freetubesubs
		importFreetube
		return;
	fi
	echo "$(jq -r '.subscriptions[] | .id + "," + .name' $freetubesubs)" >> $subsfile
	echo -e "\nImported" $(jq -r '.subscriptions[] | .id' $freetubesubs | wc -l) items
	read -n 1 -t 1
}

importYoutube () {
	echo -ne "path to youtube subs >"
	read youtubesubs
	while [ ! -f "$youtubesubs" ]; do
		echo "Cannot find file $youtubesubs"
		echo -ne "Enter a different file to try >"
		read youtubesubs
	done
	jq -r '.[] | .snippet.resourceId.channelId + "," + .snippet.title' $youtubesubs >> $subsfile
	echo -e "\nImported" $(jq -r '.[] | .snippet.channelId' $youtubesubs | wc -l) items
	read -n 1 -t 1
}

invidiousGet () {
	get $instance/api/v1/$1
	return $?
}

get () {
	hash=$(echo $1 | md5sum | cut -c 1-32)
	if [ ! -f "$cachedir/$hash.json" ]; then
		wget $1 -O $cachedir/$hash.json -a $cachedir/logs
		error=$?
		if [ ! $error -eq 0 ]; then
			rm $cachedir/$hash.json
			return $error
		fi;
	fi
	cat $cachedir/$hash.json
}

invidiousRefresh () {
	refresh $instance/api/v1/$1
}

refresh () {
	hash=$(echo $1 | md5sum | cut -c 1-32)
	filename="$cachedir/$hash.json"
	if [ -f $filename ]; then
		rm $filename
	fi
}

searchChannel () {
	echo -ne "\nSearching for a channel. Blank to cancel.\e[1F? "
	read channelSearch
	channelSearch=$(echo $channelSearch | sed 's/ /+/g')
	if [ ! -z $channelSearch ]; then
		pickChannel "search?q=$channelSearch&type=channel" 0
	fi
}

searchVideo () {
	echo -ne "\nSearching for a video. Blank to cancel.\e[1F/ "
	read search
	search=$(echo $search | sed 's/ /+/g')
	if [ ! -z $search ]; then
		pickVideo "search?q=$search&type=video" 0
	fi
}

pickChannel () {
	heading "Loading $1"
	channelsData=$(invidiousGet $1)
	error=$?
	if [ ! $error -eq 0 ]; then
		echo "wget error $error. Maybe try another instance."
		read -n 1
		return $error
	fi
	pickChannelJSON "$(jq -r '.' <<< $channelsData)" ||
	invidiousRefresh $1
}

pickChannelJSON () {
	heading "Pick a channel"
	options=$(jq -r '.[] | .author + "\t" + (.subCount | tostring) + "\t" + .authorId' <<< $1 |
		column -t -s$'\t')

	while true; do
	id=$(echo "$options" | $menu | sed 's/^\(.*\) \(.*\)$/\2/')
	if [ -z "$id" ]; then
		break
	fi
	channelMenu $id
	done
}

heading () {
	echo -e $clear$blue$bold$1$white
}

channelMenu () {
	heading "Loading... channels/$1"
	channelData=$(invidiousGet channels/$1)
	error=$?
	if [ ! $error -eq 0 ]; then
		echo "wget error $error. Maybe try another instance."
		read -n 1
		return $error
	fi
	channelName=$(echo $channelData | jq -r '.author')
	heading "$channelName Channel"
	echo $1
	grep -Fq "$1" "$subsfile" && echo "Subscribed" || echo "Not Subscribed"
	echo -ne $white
	echo $channelData | jq -r '.description'
	echo -ne "
What would you like to see?
l) Latest Videos
v) Popular Videos
p) Playlists
w) Subscribe
q) Back

> "
	read -n 1 option
	case $option in
		,) settingsMenu ;
		   channelMenu $1 ;;
		/) searchVideo ; channelMenu $1 ;;
		'?') searchChannel ; channelMenu $1 ;;
		l) pickVideo "channels/videos/$1?sort_by=newest"
		   channelMenu $1
		;;
		v) pickVideo "channels/videos/$1?sort_by=popular"
		   channelMenu $1
		;;
		p) playlists $1 ; channelmenu $1 ;;
		w) echo "$1,$channelName" >> $subsfile
		   channelMenu $1
		;;
		r) invidiousRefresh channels/$1 ""
		   channelMenu $1
		   ;;
		$'\e'|'q') return ;;
		*) channelMenu $1 ;;
	esac
}

playlists () {
	heading Loading "channels/$1/playlists"
	playlistData=$(invidiousGet "channels/$1/playlists")
	error=$?
	if [ ! $error -eq 0 ]; then
		echo "wget error $error. Maybe try another instance."
		read -n 1
		return $error
	fi
	choice=$( echo $playlistData | jq -r '.playlists[] | .title + "\t" + .playlistId' | column -t -s$'\t' | fzf | awk '{ print $NF }' )
	playlistMenu $choice
}

playlistMenu () {
	heading Loading "playlists/$1"
	playlistData=$(invidiousGet "playlists/$1")
	error=$?
	if [ ! $error -eq 0 ]; then
		echo "wget error $error. Maybe try another instance."
		read -n 1
		return $error
	fi
	while true; do
		choice=$( echo $playlistData | jq -r '.videos[] | .title + "\t" + .videoId' | column -t -s$'\t' | fzf | awk '{ print $NF }' )
		if [ -z "$choice" ]; then
			break
		fi
		watchVideo $choice
	done
}

# TODO: multiple pages of videos
pickVideo () {
	heading Loading $1
	data=$(invidiousGet $1)
	error=$?
	if [ ! $error -eq 0 ]; then
		echo "wget error $error. Maybe try another instance."
		read -n 1
		return $error
	fi
	pickVideoJSON "$(jq -r '.' <<< $data)"
}

pickVideoJSON () {
	heading "Pick a video"
	options=$(jq -r '.[] | (.title) + "\t" +
		(.author) + "\t" +
		(if (.lengthSeconds >= 3600) then
			(.lengthSeconds/3600 | floor | tostring) + ":" + (if (.lengthSeconds%3600/60<=10) then "0" else "" end) + (.lengthSeconds%3600/60 | floor | tostring) + ":" + (if (.lengthSeconds%60<=10) then "0" else "" end) + (.lengthSeconds%60 | tostring)
		else
			(.lengthSeconds/60 | floor | tostring) + ":" + (if (.lengthSeconds%60<=10) then "0" else "" end) + (.lengthSeconds%60 | tostring)
		end) + "\t" +
		(if .viewCount > 1000000 then
			(.viewCount / 1000000 | floor | tostring) + "M"
		elif .viewCount > 1000 then
			(.viewCount / 1000 | floor | tostring) + "K"
		else (.viewCount | tostring)
		end + "\t" + .videoId)' <<< $1 |
	column -t -s$'\t' -R3,4)

	while true; do
	id=$(echo "$options" | $menu | sed 's/^\(.*\) \(.*\)$/\2/')
	if [ -z "$id" ]; then
		break
	fi
	watchVideo $id
	done
}

watchVideo () {
	if [ -z "$1" ]; then
		return
	fi
	url="$videoInstance/watch?v=$1"
	if [ $playimmediate = true ]; then
		$videoplayer $url &
	fi

	if [ -z "$2" ]; then
		heading "Loading $instance/api/v1/videos/$1 ..."
		videoData=$(invidiousGet "videos/$1")
		error=$?
		if [ ! $error -eq 0 ]; then
			echo "wget error $error. Maybe try another instance."
			read -n 1
			return $error
		fi
	else
		videoData=$2
	fi
	while true; do
	heading "$(echo $videoData | jq -r '.title')"
	echo $url
	echo -ne "
p) Play Video
d) description
c) View Channel
n) Related Videos
r) Refresh page
q) Back

> "
	read -n 1 vidNum
	case $vidNum in
		,) settingsMenu ;;
		/) searchVideo;;
		'?') searchChannel ;;
		c)
			channelMenu $(jq -r '.authorId' <<< $videoData)
			;;
		n)
			pickVideoJSON "$(jq -r '.recommendedVideos' <<<$videoData)"
			;;
		d)
			echo $videoData | jq -r '.description' | less
			;;
		p)
			echo -e "\nStarting Video"
			$videoplayer $url &
			;;
		r)
			invidiousRefresh videos/$1
			watchVideo $1 $2
			break
			;;
		$'\e'|'q') break ;;
		*) echo 'try again' ;;
	esac
	done
}

subscriptionsMenu () {
	heading Subscriptions

	echo -ne "
What do you want to do?
c) View Subscribed Channels
l) Latest Videos
r) Reload Videos
q) Exit

> "
	read -n 1 option
	case $option in
		,) settingsMenu ; subscriptionsMenu ;;
		/) searchVideo ; subscriptionsMenu ;;
		'?') searchChannel ; subscriptionsMenu ;;
		c) subscribedChannels ; subscriptionsMenu ;;
		l) subscriptionsLatest ; subscriptionsMenu ;;
		r) subscriptionsReload ; subscriptionsMenu ;;
		$'\e'|'q') echo -ne $clear ;;
	esac
}

subscriptionsReload () {
	heading Reloading
	echo -e "sfeedpath=$subsfeedsdir \n feeds() {" > $subsfeeds
	cat $configdir/subscriptions.csv | \
		awk -F ',' '{print "feed \""$1"\"" " \"'$RSSinstance'/feed/channel/"$1"\""}' \
		>> $subsfeeds
	echo "}" >> $subsfeeds

	sfeed_update $subsfeeds
}

subscriptionsLatest () {
	while true; do
	if [ -z "$1" ]; then
		choice=$(awk -F'\t' '!seen[$6]++ {OFS="\t|\t";
			$1=strftime("%Y-%m-%d", $1);
			sub(".*/", "", FILENAME);
			channelid = FILENAME;
			print $1, $7, $2, $3, channelid}' "$subsfeedsdir/"* | sort -r | column -t -s$'\t' -T4 | $menu)
	else
		choice=$(awk -F'\t' '!seen[$6]++ {OFS="\t|\t";
			$1=strftime("%Y-%m-%d", $1);
			sub(".*/", "", FILENAME);
			channelid = FILENAME;
			print $1, $7, $2, $3, channelid}' "$subsfeedsdir/$1" | sort -r | column -t -s$'\t' -T4 | $menu)
	fi
	if [ ! -z "$choice" ]; then
		watchVideo "$(sed 's@^.*/watch?v=\(.*\)@\1@' <<< $choice)" \
			"{	\"title\":\"$(awk -F'|' '{ print $3 }' <<< $choice)\",
					\"authorId\":\"$(awk -F'|' '{ print $5 }' <<< $choice)\"}"
	else
		break
	fi
	done
}

subscribedChannels () {
	while true; do
		heading Subscriptions
		subs=$(cat $configdir/subscriptions.csv)
		channel=$(awk -F, '{OFS="\t"; print $1, $2 }' <<< $subs | column -t -s$'\t' | $menu | awk '{print $1}')
		if [ ! -z "$channel" ]; then
			subscriptionsLatest "$channel" || channelMenu "$channel"
		else
			break
		fi
	done
}

menu () {
	echo -ne $clear$bold$red
	cat $art
	echo -ne $white

	echo -ne "
What do you want to do?
,) Settings
?) Search for channel
/) Search for video
s) Subscriptions
q) Exit

> "
	read -n 1 option
	case $option in
		,) settingsMenu ; menu ;;
		'?') searchChannel ; menu ;;
		/) searchVideo ; menu ;;
		s) subscriptionsMenu ; menu ;;
		r) sfeed_update $subsfeeds ;;
		$'\e'|'q') echo -ne $clear ; exit ;;
		*) menu ;;
	esac
}

if [ ! -z $1 ]; then
	if [[ $1 =~ [-_a-zA-Z0-9]{11} ]]; then
		watchVideo $BASH_REMATCH
		menu
		exit
	fi

	echo "could not find video ID in $1"
	read -n 1
	exit
else
	menu
fi
